package com.shenke.IH_backend.aop;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.nacos.api.annotation.NacosInjected;
import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.exception.NacosException;
import com.shenke.IH_backend.annotation.LimitCheck;
import com.shenke.IH_backend.common.ErrorCode;
import com.shenke.IH_backend.exception.BusinessException;
import com.shenke.IH_backend.manager.CounterManager;
import com.shenke.IH_backend.model.entity.User;
import com.shenke.IH_backend.service.UserService;
import com.shenke.IH_backend.utils.NetUtils;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.yaml.snakeyaml.Yaml;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

/**
 * 接口限流 AOP
 * @author <a href="https://gitee.com/shenketongxue">森克同学</a>
 **/
@Aspect
@Component
public class LimitAspect {

    @Resource
    private CounterManager counterManager;

    @Resource
    private UserService userService;

    @NacosInjected
    private ConfigService configService;

    @Before("@annotation(limitCheck)")
    public void beforeMethod(LimitCheck limitCheck){
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        // 显式获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        if (loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
        }
        String ipAddress = NetUtils.getIpAddress((HttpServletRequest) request);
        crawlerDetect(loginUser.getId(), ipAddress);
    }

    private void crawlerDetect(long loginUserId, String ip) {
        final int WARN_COUNT = 10;
        final int BAN_COUNT = 20;
        String key = String.format("user:access:%s", loginUserId);
        // 一分钟内访问次数，180 秒过期
        long count = counterManager.incrAndGetCounter(key, 1, TimeUnit.MINUTES, 180);
        if (count > BAN_COUNT) {
            //踢下线
            StpUtil.kickout(loginUserId);
            //更新ip黑名单
            updateNacosBlackList(ip);
            // 封号
            User updateUser = new User();
            updateUser.setId(loginUserId);
            updateUser.setUserRole("ban");
            userService.updateById(updateUser);
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "访问太频繁，已被封号");
        }
        if (count > WARN_COUNT) {
            throw new BusinessException(110, "警告访问太频繁");
        }
    }

    @Value("${nacos.config.data-id}")
    private String dataId;

    //@Value("${nacos.config.server-addr}")
    //private String serverAddr;

    @Value("${nacos.config.group}")
    private String group;

    private void updateNacosBlackList(String ip){
        Properties properties = new Properties();
        //properties.put("serverAddr", serverAddr);
        try {
            //ConfigService configService = NacosFactory.createConfigService(properties);
            String content = configService.getConfig(dataId, group, 5000);
            Yaml yaml = new Yaml();
            Map<String, Object> yamlMap = yaml.load(content);
            List<String> blacklist = (List<String>) yamlMap.get("blackIpList");
            if (!blacklist.contains(ip)) {
                blacklist.add(ip);
            }
            yamlMap.put("blackIpList", blacklist);
            String updatedContent = yaml.dump(yamlMap);
            boolean isPublishOk = configService.publishConfig(dataId, group, updatedContent);
            if (isPublishOk) {
                System.out.println("配置更新成功");
            } else {
                System.out.println("配置更新失败");
            }
        } catch (NacosException e) {
            e.printStackTrace();
        }
    }
}
